package com.llyb.tingshu.search.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.aggregations.LongTermsBucket;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import co.elastic.clients.json.JsonData;
import com.alibaba.fastjson.JSON;
import com.llyb.tingshu.album.client.AlbumInfoFeignClient;
import com.llyb.tingshu.album.client.CategoryFeignClient;
import com.llyb.tingshu.common.constant.RedisConstant;
import com.llyb.tingshu.common.execption.GuiguException;
import com.llyb.tingshu.common.result.Result;
import com.llyb.tingshu.common.util.PinYinUtils;
import com.llyb.tingshu.model.album.AlbumInfo;
import com.llyb.tingshu.model.album.BaseCategory1;
import com.llyb.tingshu.model.album.BaseCategory3;
import com.llyb.tingshu.model.album.BaseCategoryView;
import com.llyb.tingshu.model.search.AlbumInfoIndex;
import com.llyb.tingshu.model.search.AttributeValueIndex;
import com.llyb.tingshu.model.search.SuggestIndex;
import com.llyb.tingshu.model.user.UserInfo;
import com.llyb.tingshu.query.search.AlbumIndexQuery;
import com.llyb.tingshu.search.repo.AlbumIndexRepository;
import com.llyb.tingshu.search.repo.SuggestIndexRepository;
import com.llyb.tingshu.search.service.SearchService;
import com.llyb.tingshu.user.client.UserInfoFeignClient;
import com.llyb.tingshu.vo.album.AlbumStatVo;
import com.llyb.tingshu.vo.search.AlbumInfoIndexVo;
import com.llyb.tingshu.vo.search.AlbumSearchResponseVo;
import com.llyb.tingshu.vo.user.UserInfoVo;
import io.micrometer.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Function;
import java.util.stream.Collectors;


@Slf4j
@Service
@RefreshScope
@SuppressWarnings({"all"})
public class SearchServiceImpl implements SearchService {


    @Autowired
    private AlbumIndexRepository albumIndexRepository;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private CategoryFeignClient categoryFeignClient;

    @Autowired
    private ElasticsearchClient elasticsearchClient;

    @Autowired
    private SuggestIndexRepository suggestIndexRepository;

    @Value("${ranking.dimension.array}")
    private String rankingArray;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public void upperAlbum(Long albumId) {

        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();


        // 获取 albumInfo
        CompletableFuture<AlbumInfo> albumInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {
            // 赋值 专辑信息
            Result<AlbumInfo> albumInfoR = albumInfoFeignClient.getAlbumInfo(albumId);
            Assert.notNull(albumInfoR, "远程调用获取专辑信息失败");
            AlbumInfo albumInfo = albumInfoR.getData();
            Assert.notNull(albumInfo, "远程调用获取专辑信息为空");
            BeanUtils.copyProperties(albumInfo, albumInfoIndex);

            // 赋值 属性列表
            // 获取 album_attribute_value
            if (!CollectionUtils.isEmpty(albumInfo.getAlbumAttributeValueVoList())) {
                List<AttributeValueIndex> attributeValueIndexList = albumInfo.getAlbumAttributeValueVoList().stream().map(albumAttributeValue -> {
                    AttributeValueIndex attributeValueIndex = new AttributeValueIndex();
                    attributeValueIndex.setAttributeId(albumAttributeValue.getAttributeId());
                    attributeValueIndex.setValueId(albumAttributeValue.getValueId());
                    return attributeValueIndex;
                }).collect(Collectors.toList());
                albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);
            }

            return albumInfo;
        }, threadPoolExecutor).exceptionally(throwable -> {
            log.error("远程调用赋值专辑信息失败:{}", throwable.getMessage());
            return null;
        });


        // 根据三级分类id 获取所有分类信息
        CompletableFuture<Integer> categoryComplerableFuture = albumInfoCompletableFuture.thenApplyAsync(albumInfo -> {
            Result<BaseCategoryView> baseCategoryViewR = categoryFeignClient.getCategoryView(albumInfo.getCategory3Id());
            Assert.notNull(baseCategoryViewR, "远程调用获取分类信息失败");
            BaseCategoryView baseCategoryView = baseCategoryViewR.getData();
            Assert.notNull(baseCategoryView, "远程调用获取分类信息为空");
            albumInfoIndex.setCategory1Id(baseCategoryView.getCategory1Id());
            albumInfoIndex.setCategory2Id(baseCategoryView.getCategory2Id());

            return 1;
        }, threadPoolExecutor).exceptionally(throwable -> {
            log.error("远程调用赋值分类信息失败:{}", throwable.getMessage());
            return 0;
        });


        // 获取 albumstat
        CompletableFuture<Integer> statCompletableFuture = CompletableFuture.supplyAsync(() -> {
            Result<AlbumStatVo> albumStatR = albumInfoFeignClient.getAlbumStat(albumId);
            Assert.notNull(albumStatR, "远程调用获取专辑统计信息失败");
            AlbumStatVo albumStatVo = albumStatR.getData();
            Assert.notNull(albumStatVo, "远程调用获取专辑统计信息失败");
            albumInfoIndex.setPlayStatNum(albumStatVo.getPlayStatNum());
            albumInfoIndex.setSubscribeStatNum(albumStatVo.getSubscribeStatNum());
            albumInfoIndex.setBuyStatNum(albumStatVo.getBuyStatNum());
            albumInfoIndex.setCommentStatNum(albumStatVo.getCommentStatNum());

            // 赋值热度值
            albumInfoIndex.setHotScore(albumInfoIndex.getPlayStatNum() * 0.3 +
                    albumInfoIndex.getSubscribeStatNum() * 0.2 +
                    albumInfoIndex.getBuyStatNum() * 0.1 +
                    albumInfoIndex.getCommentStatNum() * 0.4);

            return 1;
        }, threadPoolExecutor).exceptionally(throwable -> {
            log.error("更新专辑信息索引异常：{}", throwable.getMessage());
            return 0;
        });

        // 获取 user_info
        CompletableFuture<Integer> userCompetableFuture = albumInfoCompletableFuture.thenApplyAsync(albumInfo -> {
            Result<UserInfoVo> userInfoR = userInfoFeignClient.getUserInfo(albumInfo.getUserId());
            Assert.notNull(userInfoR, "远程调用获取用户信息失败");
            UserInfoVo userInfoVo = userInfoR.getData();
            Assert.notNull(userInfoVo, "远程调用获取用户信息失败");

            albumInfoIndex.setAnnouncerName(userInfoVo.getNickname());
            return 1;
        }, threadPoolExecutor).exceptionally(throwable -> {
            log.error("远程调用赋值用户名信息失败:{}", throwable.getMessage());
            return 0;
        });

        // 判断线程都成功
        try {
            if (null == albumInfoCompletableFuture.get() || 0 == categoryComplerableFuture.get() || 0 == statCompletableFuture.get() || 0 == userCompetableFuture.get()) {
                log.error("远程调用失败");
                throw new GuiguException(20001, "远程调用失败");
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }

        // 保存到es
        albumIndexRepository.save(albumInfoIndex);

        // 保存到 Suggestinfo库中
        // 保存标题

        SuggestIndex suggestIndex = new SuggestIndex();
        suggestIndex.setId(UUID.randomUUID().toString().replace("-", ""));
        suggestIndex.setTitle(albumInfoIndex.getAlbumTitle());
        suggestIndex.setKeyword(new Completion(new String[]{albumInfoIndex.getAlbumTitle()}));
        suggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumInfoIndex.getAlbumTitle())}));
        suggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumInfoIndex.getAlbumTitle())}));

        suggestIndexRepository.save(suggestIndex);

        // 保存简介
        suggestIndex = new SuggestIndex();
        suggestIndex.setId(UUID.randomUUID().toString().replace("-", ""));
        suggestIndex.setTitle(albumInfoIndex.getAlbumIntro());
        suggestIndex.setKeyword(new Completion(new String[]{albumInfoIndex.getAlbumIntro()}));
        suggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumInfoIndex.getAlbumIntro())}));
        suggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumInfoIndex.getAlbumIntro())}));
        suggestIndexRepository.save(suggestIndex);

        // 保存作者
        suggestIndex = new SuggestIndex();
        suggestIndex.setId(UUID.randomUUID().toString().replace("-", ""));
        suggestIndex.setTitle(albumInfoIndex.getAnnouncerName());
        suggestIndex.setKeyword(new Completion(new String[]{albumInfoIndex.getAnnouncerName()}));
        suggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumInfoIndex.getAnnouncerName())}));
        suggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumInfoIndex.getAnnouncerName())}));
        suggestIndexRepository.save(suggestIndex);

    }

    @Override
    public void lowerAlbum(Long albumId) {
        albumIndexRepository.deleteById(albumId);
    }

    @Override
    public AlbumSearchResponseVo search(AlbumIndexQuery albumIndexQuery) {
        AlbumSearchResponseVo albumSearchResponseVo = new AlbumSearchResponseVo();

        // 构建DSL查询语句
        SearchRequest searchRequest = this.queryBuildDsl(albumIndexQuery);

        SearchResponse<AlbumInfoIndex> searchResponse = null;
        try {
            searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        // 处理返回值
        albumSearchResponseVo = this.parseSearchResponse(searchResponse);
        albumSearchResponseVo.setTotal(searchResponse.hits().total().value());
        albumSearchResponseVo.setPageNo(albumIndexQuery.getPageNo());
        albumSearchResponseVo.setPageSize(albumIndexQuery.getPageSize());

//        albumSearchResponseVo.setTotalPages(searchResponse.hits().total().relation() == TotalHitsRelation.Eq ?)
        albumSearchResponseVo.setTotalPages((albumSearchResponseVo.getTotal()+
                albumSearchResponseVo.getPageSize()-1)/albumSearchResponseVo.getPageSize());

        return albumSearchResponseVo;
    }

    @Override
    public List<String> completeSuggest(String keyword) {
        SearchResponse<SuggestIndex> searchResponse = null;

        try {
            searchResponse = elasticsearchClient.search(s -> s.index("suggestinfo").suggest(s1 -> s1
                            .suggesters("suggestion_keyword", s3 -> s3
                                    .prefix(keyword)
                                    .completion(c -> c.field("keyword")
                                            .skipDuplicates(true).size(10).fuzzy(f -> f.fuzziness("auto"))))
                            .suggesters("suggestion_keywordPinyin", s3 -> s3
                                    .prefix(keyword)
                                    .completion(c -> c.field("keywordPinyin")
                                            .skipDuplicates(true).size(10).fuzzy(f -> f.fuzziness("auto"))))
                            .suggesters("suggestion_keywordSequence", s3 -> s3
                                    .prefix(keyword)
                                    .completion(c -> c.field("keywordSequence")
                                            .skipDuplicates(true).size(10).fuzzy(f -> f.fuzziness("auto")))))
                    , SuggestIndex.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        ArrayList<String> list = new ArrayList<>();
        // 批量获取list
        list.addAll(this.getResponseList(searchResponse,"suggestion_keyword"));
        list.addAll(this.getResponseList(searchResponse,"suggestion_keywordPinyin"));
        list.addAll(this.getResponseList(searchResponse,"suggestion_keywordSequence"));

        return list;
    }

    @Override
    public List<Map<String, Object>> channel(Long category1Id) {

        ArrayList<Map<String, Object>> listResult = new ArrayList<>();

        Result<List<BaseCategory3>> topBaseCategory3R = categoryFeignClient.findTopBaseCategory3(category1Id);
        Assert.notNull(topBaseCategory3R, "远程调用获取一级分类下的所有3级分类失败");
        List<BaseCategory3> topBaseCategory3List = topBaseCategory3R.getData();
        Assert.notNull(topBaseCategory3List, "获取一级分类下的所有3级分类为空");

        // 获取3级id集合
        List<Long> category3IdList = topBaseCategory3List.stream().map(BaseCategory3::getId).collect(Collectors.toList());
        // 由于lamb表达式需要的是fieldvalue, 所以不能使用list<Long>,需要转换
        // q->q.terms(t->t.field("category3Id").terms(t->t.value(category3IdList) 报错
        List<FieldValue> fieldValueCategory3List = category3IdList.stream()
                .map(category3Id -> FieldValue.of(category3Id)).collect(Collectors.toList());

        // 可以根据3级分类id查询
        Map<Long, BaseCategory3> longBaseCategory3Map = topBaseCategory3List.stream()
                .collect(Collectors.toMap(BaseCategory3::getId, Function.identity()));

        SearchResponse<AlbumInfoIndex> searchResponse = null;
        try {
            searchResponse = elasticsearchClient.search(s -> s.index("albuminfo")
                            .query(q -> q.terms(t -> t.field("category3Id").terms(t1 -> t1.value(fieldValueCategory3List))))
                            .aggregations("aggress_category3Id", a -> a
                                    .terms(t -> t.field("category3Id").size(10))
                                    .aggregations("topSixHotScoreAgg", a1 -> a1.topHits(t -> t.size(6)
                                            .sort(s2 -> s2.field(f -> f.field("hotScore").order(SortOrder.Desc))))))
                    , AlbumInfoIndex.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


        // 处理响应值 变成
        // //	根据三级分类Id 找到三级分类对象{category3Id, categoryName}
        //map.put("baseCategory3", category3IdToMap.get(category3Id));
        ////	专辑的集合数据
        //map.put("list", albumInfoIndexList);

        Aggregate aggressCategory3Id = searchResponse.aggregations().get("aggress_category3Id");

        // 判空
        if (aggressCategory3Id != null) {
            List<LongTermsBucket> array = aggressCategory3Id.lterms().buckets().array();
            array.stream().forEach(longRareTermsBucket -> {
                Map<String, Object> singleMap = new HashMap<>();
                singleMap.put("baseCategory3", longBaseCategory3Map.get(longRareTermsBucket.key()));

                List<Hit<JsonData>> topSixHotScoreAgg = longRareTermsBucket.aggregations()
                        .get("topSixHotScoreAgg").topHits().hits().hits();

                ArrayList<AlbumInfoIndex> albumInfoIndexArrayList = new ArrayList<>();
                topSixHotScoreAgg.stream().forEach(hit -> {
                    String json = hit.source().toString();
                    AlbumInfoIndex albumInfoIndex = JSON.parseObject(json, AlbumInfoIndex.class);

                    albumInfoIndexArrayList.add(albumInfoIndex);
                });
                singleMap.put("list", albumInfoIndexArrayList);
                listResult.add(singleMap);
            });
        }


        return listResult;
    }

    /**
     * 根据专辑 id 获取专辑信息
     * @param albumId
     * @return
     *         albumInfo
     *         albumStatVo
     *         announcer
     *         baseCategoryView
     */
    @Override
    public Map<String,Object> getItem(Long albumId) {
        // 使用布隆过滤器，防止缓存穿透
        // todo 等 mysql和redis 数据一致性，可放开
        /*RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
        if (!bloomFilter.contains(albumId)) {
            log.error("专辑不存在:{}", albumId);
            return null;
        }*/

        Map<String, Object> resultMap = new HashMap<>();


        // 获取专辑信息
        CompletableFuture<AlbumInfo> albumInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {

            Result<AlbumInfo> albumInfoR = albumInfoFeignClient.getAlbumInfo(albumId);
            Assert.notNull(albumInfoR, "远程调用获取专辑信息失败");
            AlbumInfo albumInfo = albumInfoR.getData();
            Assert.notNull(albumInfo, "获取专辑信息为空");
            // 赋值
            HashMap<String, Object> singleMap = new HashMap<>();
            resultMap.put("albumInfo", albumInfo);
            return albumInfo;
        }, threadPoolExecutor).whenCompleteAsync((albumInfo, throwable) -> {
            if (throwable != null) {
                log.error("远程调用获取专辑信息失败:{}", throwable.getMessage());
            }
        });

        // 获取专辑统计信息
        CompletableFuture<Void> albumStatCompletableFuture = CompletableFuture.runAsync(() -> {

            Result<AlbumStatVo> albumStatR = albumInfoFeignClient.getAlbumStat(albumId);
            Assert.notNull(albumStatR, "远程调用获取专辑统计信息失败");
            AlbumStatVo albumStatVo = albumStatR.getData();
            Assert.notNull(albumStatVo, "获取专辑统计信息为空");
            // 赋值
            HashMap<String, Object> singleMap = new HashMap<>();
            resultMap.put("albumStatVo", albumStatVo);
        },threadPoolExecutor).whenCompleteAsync((albumInfo, throwable) -> {
            if (throwable != null) {
                log.error("远程调用获取统计失败:{}", throwable.getMessage());
            }
        });


        // 获取专辑 announcer
        CompletableFuture<Void> announcerCompletableFuthre = albumInfoCompletableFuture.thenAcceptAsync(albumInfo -> {
            Result<UserInfoVo> userInfoR = userInfoFeignClient.getUserInfo(albumInfo.getUserId());
            Assert.notNull(userInfoR, "远程调用获取专辑作者信息失败");
            UserInfoVo userInfoVo = userInfoR.getData();
            Assert.notNull(userInfoVo, "获取专辑作者信息为空");

            UserInfo userInfo = new UserInfo();
            HashMap<String, Object> singleMap = new HashMap<>();
            resultMap.put("announcer", userInfoVo);
        }, threadPoolExecutor).whenCompleteAsync((albumInfo, throwable) -> {
            if (throwable != null) {
                log.error("远程调用获取专辑作者信息失败:{}", throwable.getMessage());
            }
        });


        // 获取专辑 baseCategoryView
        CompletableFuture<Void> baseCategoryViewCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(albumInfo -> {
            Result<BaseCategoryView> baseCategoryViewR = categoryFeignClient.getCategoryView(albumInfo.getCategory3Id());
            Assert.notNull(baseCategoryViewR, "远程调用获取专辑分类信息失败");
            BaseCategoryView baseCategoryView = baseCategoryViewR.getData();
            Assert.notNull(baseCategoryView, "获取专辑分类信息为空");

            HashMap<String, Object> singleMap = new HashMap<>();
            resultMap.put("baseCategoryView", baseCategoryView);
        },threadPoolExecutor).whenCompleteAsync((albumInfo, throwable) -> {
            if (throwable != null) {
                log.error("远程调用获取专辑分类信息失败:{}", throwable.getMessage());
            }
        });


        CompletableFuture.allOf(albumStatCompletableFuture,
                albumStatCompletableFuture,
                announcerCompletableFuthre,
                baseCategoryViewCompletableFuture).join();


        return resultMap;
    }


    private List<String> getResponseList(SearchResponse<SuggestIndex> searchResponse, String keyword) {
        List<Suggestion<SuggestIndex>> suggestions = searchResponse.suggest().get(keyword);

        if (!CollectionUtils.isEmpty(suggestions)) {
            for (Suggestion<SuggestIndex> suggestion : suggestions) {
                return suggestion.completion().options().stream()
                        .map(option -> option.source().getTitle())
                        .collect(Collectors.toList());
            }
        }
        return new ArrayList<>();
    }

    private AlbumSearchResponseVo parseSearchResponse(SearchResponse<AlbumInfoIndex> searchResponse) {

        AlbumSearchResponseVo albumSearchResponseVo = new AlbumSearchResponseVo();
        if (!CollectionUtils.isEmpty(searchResponse.hits().hits())) {
            searchResponse.hits().hits().forEach(hit -> {
                AlbumInfoIndex source = hit.source();
                AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
                BeanUtils.copyProperties(source,albumInfoIndexVo);

                // 赋值高亮
                Map<String, List<String>> highlight = hit.highlight();
                List<String> strings = highlight.get("albumTitle");
                if (!CollectionUtils.isEmpty(strings)) {
                    albumInfoIndexVo.setAlbumTitle(strings.get(0));
                }
                albumSearchResponseVo.getList().add(albumInfoIndexVo);
            });
        }

        return albumSearchResponseVo;
    }

    private SearchRequest queryBuildDsl(AlbumIndexQuery albumIndexQuery) {

        SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder();
        BoolQuery.Builder boolQueueBuilder = new BoolQuery.Builder();

        searchRequestBuilder.index("albuminfo");
        // 是否有title,intro,nama
        if (!StringUtils.isEmpty(albumIndexQuery.getKeyword())) {
            boolQueueBuilder.must(f->f.bool(b->b
                    .should(s->s
                            .match(m->m.field("albumTitle").query(albumIndexQuery.getKeyword())))
                    .should(s->s
                            .match(m->m.field("albumIntro").query(albumIndexQuery.getKeyword())))
                    .should(s->s
                            .match(m->m.field("announcerName").query(albumIndexQuery.getKeyword())))));

            // 设置高亮
            searchRequestBuilder.highlight(h->h
                    .fields("albumTitle",hf->hf.preTags("<span style=color:red>").postTags("</span>")));
        }

        // 是否有分类id
        if (null != albumIndexQuery.getCategory1Id()) {
            boolQueueBuilder.filter(f->f.term(t->t.field("category1Id").value(albumIndexQuery.getCategory1Id())));
        }
        if (null != albumIndexQuery.getCategory2Id()) {
            boolQueueBuilder.filter(f->f.term(t->t.field("category2Id").value(albumIndexQuery.getCategory2Id())));
        }
        if (null != albumIndexQuery.getCategory3Id()) {
            boolQueueBuilder.filter(f->f.term(t->t.field("category3Id").value(albumIndexQuery.getCategory3Id())));
        }

        // 是否有属性和属性值id
        List<String> attributeList = albumIndexQuery.getAttributeList();
        if (!CollectionUtils.isEmpty(attributeList)) {
            for (String attribute : attributeList) {
                String[] split = attribute.split(":");
                boolQueueBuilder.filter(f->f.nested(n->n.path("attributeValueIndexList")
                        .query(q->q.bool(b->b.must(m->m.term(t->t.field("attributeValueIndexList.attributeId").value(split[0])))
                                .must(m->m.term(t->t.field("attributeValueIndexList.valueId").value(split[1])))
                                ))));
            }
        }

        // 分页
        Integer from = (albumIndexQuery.getPageNo()-1)*albumIndexQuery.getPageSize();
        searchRequestBuilder.from(from);
        searchRequestBuilder.size(albumIndexQuery.getPageSize());

        // 设置根据相似度排行  (应该优先相似度，还是优先前端传来的排序关键词，自己决定。本例采用的是优先相似度)
        searchRequestBuilder.sort(s->s.field(f->f.field("_score").order(SortOrder.Desc)));

        // order=1:asc  排序规则   0:asc   排序（综合排序[1:desc] 播放量[2:desc] 发布时间[3:desc]；asc:升序 desc:降序）
        if (!StringUtils.isEmpty(albumIndexQuery.getOrder())) {
            String[] split = albumIndexQuery.getOrder().split(":");
            if (split != null && split.length == 2) {
                String filed = "hotScore";
                switch (split[0]) {
                    case "1" ->
                        // 综合排序
                            filed = "hotScore";
                    case "2" ->
                            filed = "playStatNum";
                    case "3" ->
                            filed = "createTime";
                }
                String finalFiled = filed;
                searchRequestBuilder.sort(s->s.field(f->f.field(finalFiled).order("asc".equals(albumIndexQuery.getOrder())? SortOrder.Asc: SortOrder.Desc)));
            }

        }



        searchRequestBuilder.query(boolQueueBuilder.build()._toQuery());
        SearchRequest searchRequest = searchRequestBuilder.build();
        System.out.println("DSL： \t" + searchRequest.toString());
        return searchRequest;
    }

    /**
     * 查询排行榜信息
     * @param category1Id
     * @param ranking
     * @return
     */
    @Override
    public List<AlbumInfoIndexVo> findRankingList(Long category1Id, String ranking) {
        List<AlbumInfoIndexVo> list = (List<AlbumInfoIndexVo>) redisTemplate.opsForHash().get(RedisConstant.RANKING_KEY_PREFIX + category1Id, ranking);
        return list;
    }

    @Override
    public void updateLatelyAlbumRanking() {

        // 查询所有一级分类id
        Result<List<BaseCategory1>> baseCategory1Result = albumInfoFeignClient.findAllCategory1();
        Assert.notNull(baseCategory1Result, "查询一级分类数据失败");
        List<BaseCategory1> baseCategory1List = baseCategory1Result.getData();
        Assert.notNull(baseCategory1List, "查询一级分类数据失败");

        for (BaseCategory1 baseCategory1 : baseCategory1List) {

            String[] split = rankingArray.split(",");
            for (String ranking : split) {
                // 根据 一级分类，从es 中查询五个维度并保存到redis中
                SearchResponse<AlbumInfoIndex> searchResponse = null;
                try {
                    searchResponse = elasticsearchClient.search(s -> s.index("albuminfo")
                                    .query(q -> q.term(t -> t.field("category1Id").value(baseCategory1.getId())))
                                    .sort(s1 -> s1.field(f -> f.field(ranking).order(SortOrder.Desc)))
                                    .size(20)
                            ,
                            AlbumInfoIndex.class
                    );
                    List<AlbumInfoIndex> albumInfoIndexList = searchResponse.hits().hits()
                            .stream().map(Hit::source).collect(Collectors.toList());

                    redisTemplate.opsForHash()
                            .put(RedisConstant.RANKING_KEY_PREFIX+baseCategory1.getId(),ranking,albumInfoIndexList);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }

        }
    }
}
